using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Storage;
using System.Xml.Serialization;
using System.Xml;

namespace Urban_Flash.Dependencies
{
    public class Input
    {
        #region Fields
        GamePadState currentGamePadState;
        GamePadState previousGamePadState;
        GameTime gameTime;
        List<KeyPress> ButtonsHeld = new List<KeyPress>();
        #endregion

        #region Properties

        /// <summary>
        /// Return a Vector2 determing how much the Left thumbstick is being pressed in any direction.
        /// </summary>
        public Vector2 LeftThumb
        {
            get { return currentGamePadState.ThumbSticks.Left; }
        }

        /// <summary>
        /// Return a Vector2 determing how much the Right thumbstick is being pressed in any direction.
        /// </summary>
        public Vector2 RightThumb
        {
            get { return currentGamePadState.ThumbSticks.Right; }
        }

        /// <summary>
        /// Is the Left thumbstick being pressed up? (Must be pressed at least half way)
        /// </summary>
        public bool MoveUp
        {
            get { return currentGamePadState.ThumbSticks.Left.Y < -0.5f; }
        }

        /// <summary>
        /// Is the Left thumbstick being pressed down? (Must be pressed at least half way)
        /// </summary>
        public bool MoveDown
        {
            get { return currentGamePadState.ThumbSticks.Left.Y > 0.5f; }
        }

        /// <summary>
        /// Is the Left thumbstick being pressed Left? (Must be pressed at least half way)
        /// </summary>
        public bool MoveLeft
        {
            get { return currentGamePadState.ThumbSticks.Left.X < -0.5f; }
        }

        /// <summary>
        /// Is the Left thumbstick being pressed Right? (Must be pressed at least half way)
        /// </summary>
        public bool MoveRight
        {
            get { return currentGamePadState.ThumbSticks.Left.X > 0.5f; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Update the states of gamepad.
        /// </summary>
        public virtual void Update(GameTime gameTime)
        {
            //Update game timer
            this.gameTime = gameTime;
            // Save the previous state of the keyboard and game pad so we can determinesingle key/button presses
            previousGamePadState = currentGamePadState;
            // Read the current state of the keyboard and gamepad and store it
            currentGamePadState = GamePad.GetState(PlayerIndex.One);
            //Update time for button held and remove buttons that are no longer being held
            UpdateHeldButtons(gameTime);
        }

        /// <summary>
        /// Is this button newly pressed? (Gets buffered input)
        /// </summary>
        /// <param name="b">The button we're checking</param>
        /// <returns>Returns if this button's newly pressed or not. (true/false)</returns>
        public bool IsNewButtonPressed(Buttons b)
        {
            bool pressed = previousGamePadState.IsButtonUp(b) && currentGamePadState.IsButtonDown(b);
            //Start timer for how long this button has been pressed
            if (pressed) AddButtonHeld(b);
            return pressed;
        }

        /// <summary>
        /// Is this button pressed? (Can be used to check if the player is holding the button down)
        /// </summary>
        /// <param name="b">The button we're checking</param>
        /// <returns>Returns if this button's held down or not. (true/false)</returns>
        public bool IsButtonPressed(Buttons b)
        {
            return currentGamePadState.IsButtonDown(b);
        }

        /// <summary>
        /// Is a button state on the gamepad pressed.
        /// </summary>
        /// <param name="b">The state of the button you want to check</param>
        /// <returns>Return if that buttons pressed or not</returns>
        public bool IsGamePadPressed(ButtonState b)
        {
            return b == ButtonState.Pressed;
        }
        #endregion

        #region Button Held Code
        class KeyPress
        {
            public Buttons button;
            public TimeSpan buttonStartTimePress;
            public TimeSpan buttonTotalTimePress;

            public void UpdateTime(GameTime gameTime)
            {
                buttonTotalTimePress = gameTime.TotalGameTime - buttonStartTimePress;
            }

            public void SetStartTime(GameTime gameTime)
            {
                buttonStartTimePress = gameTime.TotalGameTime;
            }
        }

        public void UpdateHeldButtons(GameTime gameTime)
        {
            //Iterate backwards to overcome remove error
            for (int i = (ButtonsHeld.Count - 1); i >= 0; --i)
            {
                //If this button is not being pressed remove it from the list
                if (previousGamePadState.IsButtonUp(ButtonsHeld[i].button))
                {
                    ButtonsHeld.Remove(ButtonsHeld[i]);
                }
                else
                    ButtonsHeld[i].UpdateTime(gameTime);
            }
        }

        public bool AddButtonHeld(Buttons b)
        {
            //Check if button already exsist should never happen
            for (int i = 0; i < ButtonsHeld.Count; ++i)
                if (ButtonsHeld[i].button == b)
                    return false;

            //Add new button to the list
            KeyPress keyPressed = new KeyPress();
            keyPressed.button = b;
            keyPressed.SetStartTime(gameTime);
            ButtonsHeld.Add(keyPressed);
            return true;
        }

        public TimeSpan GetTotalTimePressed(Buttons b)
        {
            for (int i = 0; i < ButtonsHeld.Count; ++i)
            {
                if (ButtonsHeld[i].button == b)
                    return ButtonsHeld[i].buttonTotalTimePress;
            }
            return TimeSpan.Zero;
        }
        #endregion

        #region Menu Methods
        /// <summary>
        /// Checks for a "menu select" input action.
        /// The controllingPlayer parameter specifies which player to read input for.
        /// If this is null, it will accept input from any player. When the action
        /// is detected, the output playerIndex reports which player pressed it.
        /// </summary>
        public bool IsMenuSelect()
        {
            return IsNewButtonPressed(Buttons.A) || IsNewButtonPressed(Buttons.Start);
        }


        /// <summary>
        /// Checks for a "menu cancel" input action.
        /// The controllingPlayer parameter specifies which player to read input for.
        /// If this is null, it will accept input from any player. When the action
        /// is detected, the output playerIndex reports which player pressed it.
        /// </summary>
        public bool IsMenuCancel()
        {
            return IsNewButtonPressed(Buttons.B) || IsNewButtonPressed(Buttons.Back);
        }


        /// <summary>
        /// Checks for a "menu up" input action.
        /// The controllingPlayer parameter specifies which player to read
        /// input for. If this is null, it will accept input from any player.
        /// </summary>
        public bool IsMenuUp()
        {
            return IsNewButtonPressed(Buttons.DPadUp) || IsNewButtonPressed(Buttons.LeftThumbstickUp);
        }


        /// <summary>
        /// Checks for a "menu down" input action.
        /// The controllingPlayer parameter specifies which player to read
        /// input for. If this is null, it will accept input from any player.
        /// </summary>
        public bool IsMenuDown()
        {
            return IsNewButtonPressed(Buttons.DPadDown) || IsNewButtonPressed(Buttons.LeftThumbstickDown);
        }


        /// <summary>
        /// Checks for a "pause the game" input action.
        /// The controllingPlayer parameter specifies which player to read
        /// input for. If this is null, it will accept input from any player.
        /// </summary>
        public bool IsPauseGame()
        {
            return IsNewButtonPressed(Buttons.Back) || IsNewButtonPressed(Buttons.Start);
        }


        #endregion
    }

    public class ActionKeyInput : Input
    {
        public Dictionary<ActionKey, Buttons> ActionList = new Dictionary<ActionKey, Buttons>();
       
        public ActionKeyInput()
        {
            LoadActionKeys();
        }

        #region Methods

        /// <summary>
        /// Must be called to load action keys
        /// </summary>
        /// <returns>returns new ActionKeyInput object</returns>
        public void LoadActionKeys()
        {
            //Read from title storage location on xbox
            string path = "Dependencies/ActionKeyList.xml";

            System.IO.Stream stream = TitleContainer.OpenStream(path);

            XmlReader reader = XmlReader.Create(stream);
            //Skip the root and read to the first element of the string
            reader.ReadToFollowing(Enum.GetName(typeof(ActionKey), 0));
            do
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    ActionKey tempKey = (ActionKey)Enum.Parse(typeof(ActionKey), reader.Name, true);
                    Buttons tempButton = (Buttons)Enum.Parse(typeof(Buttons), reader.ReadInnerXml(), true);
                    ActionList[tempKey] = tempButton;
                }
            } while (reader.Read());
        }

        /// <summary>
        /// Update the states of gamepad.
        /// </summary>
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }

        /// <summary>
        /// Is this button newly pressed? (Gets buffered input)
        /// </summary>
        /// <param name="b">The action we want to check has been activated</param>
        /// <returns>Returns if this button's newly pressed or not. (true/false)</returns>
        public bool GetBufferedActionKey(ActionKey A)
        {
            return base.IsNewButtonPressed(ActionList[A]);
        }

        /// <summary>
        /// Is this button pressed? (Can be used to check if the player is holding the button down)
        /// </summary>
        /// <param name="b">The action we want to check has been activated</param>
        /// <returns>Returns if this button's held down or not. (true/false)</returns>
        public bool GetActionKey(ActionKey A)
        {
            return base.IsButtonPressed(ActionList[A]);
        }

        public TimeSpan GetActionsTotalTimePressed(ActionKey A)
        {
            return base.GetTotalTimePressed(ActionList[A]);
        }

        #endregion

    }

    /// <summary>
    /// Always make sure the first elemnt in this enum correspond to the 1st element read to in the ActionKey Load function
    /// </summary>
    public enum ActionKey { Pause, RunLeft, RunRight, Jump, Slide, Max_Actions };

}
